#include <Precomp.h>
#include "Radar.h"
#include "defs.h"
#include "GameWorld.h"
#include "Player.h"
#include "ClientListener.h"
#include <AVUIFont.h>
#include <AVUIFormattedText.h>
#include <AVUIVisualDrawingContext.h>
#include <AVUIMouse.h>
#include <AVUIInputManager.h>

using namespace AVUI;

REGISTER_ELEMENT_BEGIN(Radar)
    REGISTER_DEFAULT_FACTORY()
REGISTER_ELEMENT_END()

Radar::Radar() : m_isFirstRender(true)
{
}

void Radar::EnsurePlanetVisuals()
{
    Font font(L"Swiss", 22.0f);

    CPlanetList& planetList = m_pGameWorld->GetPlanetList();
    for(unsigned int idx = 0; idx < planetList.size(); idx++)
    {
        CPlanet* pPlanet = planetList[idx];
        TRefCountedPtr<SolidColorBrush> pBrush = object_allocate<SolidColorBrush>();

        pBrush->set_Color(Color(GetPlanetColor(pPlanet)));

        m_planetVisuals.push_back(object_allocate<PlanetVisual>());
        PlanetVisual* pVisual = m_planetVisuals[m_planetVisuals.size() - 1];

        AddVisualChild(pVisual);

        pVisual->pBrush = pBrush;
        pVisual->pPlanet = pPlanet;
        pVisual->set_ToolTip(object_allocate<String>(pPlanet->GetName()));

        float flPlanetRenderX = pPlanet->GetXLocation() * m_flGalaxyToPixelsX;
        float flPlanetRenderY = pPlanet->GetYLocation() * m_flGalaxyToPixelsY; 
        float flPlanetRenderRadius = c_uiOrbitRadius * 3 * Float::Min(m_flGalaxyToPixelsX, m_flGalaxyToPixelsY);

        VisualDrawingContext drawingContext(pVisual);


        drawingContext.DrawEllipse(pBrush, NULL, Point(flPlanetRenderX, flPlanetRenderY), flPlanetRenderRadius, flPlanetRenderRadius);

        const char* szName = pPlanet->GetName();
        WCHAR wzShortNameBuf[4] = { 0 };

        for(unsigned int idx = 0; idx < 4; idx++)
        {
            wzShortNameBuf[idx] = (WCHAR)szName[idx];
        }


        TRefCountedPtr<FormattedText> pFormattedText = font.CreateFormattedText(wzShortNameBuf, 3, 1000.0f, false);

        float flPlanetTextRenderX = flPlanetRenderX - (pFormattedText->get_Width() / 2.0f);
        float flPlanetTextRenderY = flPlanetRenderY + flPlanetRenderRadius;

        drawingContext.DrawFormattedText(pBrush, pFormattedText, Point(flPlanetTextRenderX, flPlanetTextRenderY));
    }
}

DWORD Radar::GetPlanetColor(CPlanet* pPlanet)
{
    CGamePlayer* pPlayer = m_pGameWorld->GetPlayerForLocalPlayer();
    if(pPlayer == NULL)
    {
        return 0x0;
    }

    ETeamFlags eTeamFlag = ETeamIDToETeamFlag(pPlayer->GetTeamID());

    if(!(pPlanet->GetInfoBitmask() & eTeamFlag))
    {
        return 0xFF777777;
    }

    DWORD dwColor = ETeamIDToColor(pPlanet->GetTeamOwner());

    // Mute the colors for planets.
    DWORD dwR = (dwColor >> 16 & 0xFF) >> 1;
    DWORD dwG = (dwColor >> 8 & 0xFF) >> 1;
    DWORD dwB = (dwColor >> 0 & 0xFF) >> 1;

    return (0xFF << 24) + (dwR << 16) + (dwG << 8) + dwB;
}


void Radar::EnsurePlayerVisuals()
{
    CPlayerList& playerList = m_pGameWorld->GetPlayerList();

    for(unsigned int idx = 0; idx < playerList.size(); idx++)
    {
        UpdatePlayerLocation(EnsurePlayerVisual(playerList[idx]));
    }

}

Radar::PlayerVisual* Radar::EnsurePlayerVisual(CGamePlayer* pPlayer)
{
    PlayerVisual* pVisual = FindVisualForPlayer(pPlayer);
    
    if(pVisual == NULL)
    {
        m_playerVisuals.push_back(object_allocate<PlayerVisual>());
        pVisual = m_playerVisuals[m_playerVisuals.size() - 1];
    
        TRefCountedPtr<SolidColorBrush> pBrush = object_allocate<SolidColorBrush>();
    
        pVisual->pBrush = pBrush;
        pVisual->pPlayer = pPlayer;

        AddVisualChild(pVisual);

        UpdatePlayerState(pVisual);
        DrawPlayerVisual(pVisual);
    }

    return pVisual;
}



Radar::PlayerVisual* Radar::FindVisualForPlayer(CGamePlayer* pPlayer)
{
    for(unsigned int idx = 0; idx < m_playerVisuals.size(); idx++)
    {
        if(m_playerVisuals[idx]->pPlayer == pPlayer)
        {
            return m_playerVisuals[idx];
        }
    }
    return NULL;
}

Radar::PlanetVisual* Radar::FindVisualForPlanet(CPlanet* pPlanet)
{
    for(unsigned int idx = 0; idx < m_planetVisuals.size(); idx++)
    {
        if(m_planetVisuals[idx]->pPlanet == pPlanet)
        {
            return m_planetVisuals[idx];
        }
    }
    return NULL;
}


void Radar::DrawPlayerVisual(PlayerVisual* pVisual)
{
    CGamePlayer* pPlayer = pVisual->pPlayer;

    if(pPlayer->GetPlayerStatus() != ePlayerStatus_Alive)
    {
        return;
    }

    Font font(L"Swiss", 22.0f);

    StringPtr pString = object_allocate<AVUI::String>(pPlayer->GetIdentifier());

    TRefCountedPtr<FormattedText> pFormattedText = font.CreateFormattedText(pString->get_Ptr(), pString->get_Cch(), 1000.0f, false);

    VisualDrawingContext drawingContext(pVisual);

    drawingContext.DrawFormattedText(pVisual->pBrush, pFormattedText, Point());
}


void Radar::OnRender(DrawingContext& drawingContext)
{
    m_flGalaxyToPixelsX = (get_RenderSize().get_Width()) / (c_uiGalaxyWidth * 1.0f);
    m_flGalaxyToPixelsY = (get_RenderSize().get_Height()) / (c_uiGalaxyWidth * 1.0f);

    m_planetVisuals.clear();

    EnsurePlanetVisuals();
    EnsurePlayerVisuals();

    if(m_isFirstRender)
    {
        m_isFirstRender = false;
        m_pEventSubscription = m_pListener->GetGameWorld()->SubscribeEvents(CGameWorld::EventSubscriber(&Radar::OnGameWorldEvent, this));
    }
}

void Radar::UpdatePlayerState(PlayerVisual* pVisual)
{
    CGamePlayer* pPlayer = pVisual->pPlayer;

    if(pPlayer->GetPlayerStatus() != ePlayerStatus_Alive)
    {
        pVisual->set_VisualOpacity(0.0f);            
    }
    else
    {
        DrawPlayerVisual(pVisual);
        pVisual->set_VisualOpacity(1.0f);            

        DWORD dwColor = ETeamIDToColor(pPlayer->GetTeamID());

        if(pPlayer == m_pGameWorld->GetPlayerForLocalPlayer())
        {
            dwColor = 0xFFFFFFFF;
        }

        pVisual->pBrush->set_Color(Color(dwColor));
    }
}


void Radar::UpdatePlayerLocation(PlayerVisual* pVisual)
{
    CGamePlayer* pPlayer = pVisual->pPlayer;

    if(pPlayer->GetPlayerStatus() != ePlayerStatus_Alive)
    {
        return;
    }

    DWORD xLocation = min<DWORD>(pPlayer->GetXLocation(), c_uiGalaxyWidth);
    DWORD yLocation = min<DWORD>(pPlayer->GetYLocation(), c_uiGalaxyWidth);

    // TODO - Consider offsetting by indicator width / height
    float flPlayerRenderX = xLocation * m_flGalaxyToPixelsX - pVisual->get_SubgraphBounds().get_Width() / 2.0f;
    float flPlayerRenderY = yLocation * m_flGalaxyToPixelsY - pVisual->get_SubgraphBounds().get_Height() / 2.0f;

    pVisual->set_VisualOffset(Vector(flPlayerRenderX, flPlayerRenderY));
}

void Radar::UpdatePlanetState(PlanetVisual* pVisual)
{
    pVisual->pBrush->set_Color(GetPlanetColor(pVisual->pPlanet));
    pVisual->InvalidateBbox();
}



void Radar::OnGameWorldEvent(CGameWorldEvent* pGameWorldEvent)
{
    if(pGameWorldEvent->GetEventID() == eGameWorldEventID_PlayerLocationUpdated)
    {
        CPlayerEvent* pEvent = (CPlayerEvent *) pGameWorldEvent;
        UpdatePlayerLocation(FindVisualForPlayer(pEvent->GetPlayer()));
    }
    else if(pGameWorldEvent->GetEventID() == eGameWorldEventID_PlayerIsAlive)
    {
        CPlayerEvent* pEvent = (CPlayerEvent *) pGameWorldEvent;

        UpdatePlayerState(EnsurePlayerVisual(pEvent->GetPlayer()));
        UpdatePlayerLocation(FindVisualForPlayer(pEvent->GetPlayer()));
    }
    else if(pGameWorldEvent->GetEventID() == eGameWorldEventID_PlayerIsDead)
    {
        CPlayerEvent* pEvent = (CPlayerEvent *) pGameWorldEvent;

        UpdatePlayerState(FindVisualForPlayer(pEvent->GetPlayer()));
        UpdatePlayerLocation(FindVisualForPlayer(pEvent->GetPlayer()));
    }      
    else if(pGameWorldEvent->GetEventID() == eGameWorldEventID_PlanetResourceUpdated ||
            pGameWorldEvent->GetEventID() == eGameWorldEventID_PlanetNameLocationChanged)
    {
        CPlanetEvent* pEvent = (CPlanetEvent *) pGameWorldEvent;

        UpdatePlanetState(FindVisualForPlanet(pEvent->GetPlanet()));
    }
}

Size Radar::MeasureOverride(const Size& constraintSize)
{
    return constraintSize;
}

Size Radar::ArrangeOverride(const Size& constraintSize)
{
    return constraintSize;
}

int Radar::get_VisualChildrenCount()
{
    return m_planetVisuals.size() + m_playerVisuals.size();
}

AVUI::Visual* Radar::GetVisualChild(int idx)
{
    if(idx < m_planetVisuals.size())
    {
        return m_planetVisuals[idx];
    }
    return m_playerVisuals[idx - m_planetVisuals.size()];
}

void Radar::OnMouseDown(MouseButtonEventArgs* pArgs)
{
    if(pArgs->get_IsHandled())
    {
        return;
    }

    pArgs->set_IsHandled(true);

    Point pt = InputManager::get_Current()->get_MousePosition(this);

    CPlanet* pPlanet = m_pGameWorld->GetPlanetByLocation((DWORD)(pt.get_X() / m_flGalaxyToPixelsX), 
                                                         (DWORD)(pt.get_Y() / m_flGalaxyToPixelsY),
                                                         4.0f);
    if(pPlanet != NULL)
    {
        CPlanetEvent planetEvent(pPlanet, eGameWorldEventID_ShowPlanetDetail);
        m_pGameWorld->FireEvent(&planetEvent);
    }
}

